import { useState, useEffect, useMemo, useRef, memo } from 'react'
import useLog, { LogContext } from './useLog/index'
import useQueryTable from './useQueryTable/index'
import { useConnect, ReduxContext, useCreateStore } from './useRedux/index'
/* 
    ⚠️：如果使用了包含有useContext的自定义hooks,那么当context上下文改变
    会让使用自定义hooks的组件自动渲染
*/
function debounce(fn, time) {
	let timer = null
	return function (...args) {
		if (timer) clearTimeout(timer)
		timer = setTimeout(() => {
			fn.apply(this, args)
		}, time)
	}
}
function useDebounce(defaultValue, time) {
	const [value, changeValue] = useState(defaultValue)
	/* 对 changeValue 做防抖处理 */
	const newChange = useMemo(() => debounce(changeValue, time), [time])
	return [value, newChange]
}
/* 同步state */
function useSyncState(defaultValue) {
	const value = useRef(defaultValue) // 保存状态
	const [, forceUpdate] = useState(null) // 更新组件
	const dispatch = (fn) => {
		// 模拟更新函数
		let newValue
		if (typeof fn === 'function') {
			newValue = fn(value.current)
		} else {
			newValue = fn
		}
		value.current = newValue
		forceUpdate({}) // 强制更新
	}
	return [value, dispatch]
}
// 获取dom元素
function useGetDOM() {
	const dom = useRef()
	useEffect(() => {
		// console.log('做一些基于 dom 的操作', dom.current)
	}, [])

	return dom
}
/* 监听 props 或者 state 的变化 */
function useEffectProps(value, cb) {
	const isMounted = useRef(false)
	useEffect(() => {
		/* 防止第一次执行 */
		isMounted.current && cb?.()
	}, [value])
	useEffect(() => {
		/* 第一次挂载 */
		isMounted.current = true
	}, [])
}

function Index(props) {
	const [value, setValue] = useDebounce('', 300)
	const [data, setData] = useSyncState(0)
	const dom = useGetDOM()
	useEffectProps(props.a, () => {
		// console.log('props a 变化 :>>', props.a)
	})
	useEffectProps(props.b, () => {
		// console.log('props b 变化 :>>', props.b)
	})
	return (
		<div ref={dom} style={{ marginTop: '50px' }}>
			<span>《react 进阶实践指南》</span>
			<input
				placeholder="请输入"
				onChange={(e) => setValue(e.target.value)}
			/>
			点赞 👍 {data.current}
			<div>
				<button
					onClick={() => {
						setData((num) => num + 1)
						console.log('data :>>', data.current) // 最新值
					}}
				>
					点击
				</button>
			</div>
		</div>
	)
}

function TestUseLog() {
	const [dom, reportMessage] = useLog()
	console.log('render TestUseLog')
	return (
		<div>
			<div ref={dom}>
				<p>《react 进阶实践指南》</p>
				<button>按钮 one (内部点击)</button>
				<button>按钮 two (内部点击)</button>
				<button>按钮 three (内部点击)</button>
			</div>
			<button
				onClick={() => {
					console.log('reportMessage :>>', reportMessage)
				}}
			>
				外部点击
			</button>
		</div>
	)
}
const MemoTestLog = memo(TestUseLog) // 阻断 useState 的更新效应
function Home() {
	const [a, setA] = useState(0)
	const [b, setB] = useState(0)
	/* 子组件更新了是因为 context变化了  */
	const [value, setValue] = useState({})
	return (
		<LogContext.Provider value={value}>
			<Index a={a} b={b} />
			<button onClick={() => setA(a + 1)}>改变 props a</button>
			<button onClick={() => setB(b + 1)}>改变 props b</button>

			<MemoTestLog />
			<button
				onClick={() =>
					setValue({
						name: '《react 进阶实践指南》',
						author: '我不是外星人',
					})
				}
			>
				点击
			</button>
		</LogContext.Provider>
	)
}

/* 模拟请求数据 */
function getTableData(payload) {
	return new Promise((resolve) => {
		Promise.resolve().then(() => {
			console.log('请求参数 :>>', payload)
			resolve({
				list: [],
				total: 3,
				current: 1,
			})
		})
	})
}
function ReduxRoot() {
	const reducer = (state, action) => {
		const { type, payload } = action
		switch (type) {
			case 'setA':
				return {
					...state,
					mesA: payload,
				}

			case 'setB':
				return {
					...state,
					mesB: payload,
				}
			case 'clear':
				return { mesA: '', mesB: '' }
			default:
				return state
		}
	}
	const initState = {
		mesA: '111',
		mesB: '111',
	}
	const store = useCreateStore(reducer, initState)

	return (
		<div>
			<ReduxContext.Provider value={store}>
				<TestReduxHooks />
			</ReduxContext.Provider>
		</div>
	)
}
function TestReduxHooks() {
	const [isShow, setShow] = useState(true)
	console.log('render TestReduxHooks')
	return (
		<div>
			<TestReduxCompA />
			<TestReduxCompB />
			<TestReduxCompC />
			{isShow ? <TestReduxCompD /> : null}
			<button onClick={() => setShow(!isShow)}>动态挂载D组件</button>
		</div>
	)
}
function TestReduxCompA() {
	const [value, setValue] = useState('')
	const [state, dispatch] = useConnect((state) => ({ mesB: state.mesB }))

	return (
		<div>
			<p>组件A</p>
			<p>组件B对我说，{state.mesB}</p>
			<input
				onChange={(e) => setValue(e.target.value)}
				placeholder="对B组件说"
			/>
			<button
				onClick={() => {
					dispatch({ type: 'setA', payload: value })
					// console.log('value :>>', value, 'state :>>', state)
				}}
			>
				确定
			</button>
		</div>
	)
}
function TestReduxCompB() {
	const [value, setValue] = useState('')
	const [state, dispatch] = useConnect((state) => ({ mesA: state.mesA }))

	return (
		<div>
			<p>组件B</p>
			<p>组件A对我说，{state.mesA}</p>
			<input
				onChange={(e) => setValue(e.target.value)}
				placeholder="对A组件说"
			/>
			<button onClick={() => dispatch({ type: 'setB', payload: value })}>
				确定
			</button>
		</div>
	)
}
function TestReduxCompC() {
	const [state] = useConnect((state) => ({
		mes1: state.mesA,
		mes2: state.mesB,
	}))
	return (
		<div>
			<p>组件A：{state.mes1}</p>
			<p>组件B：{state.mes2}</p>
		</div>
	)
}

function TestReduxCompD() {
	const [, dispatch] = useConnect()
	console.log('D 组件更新')
	return (
		<div>
			<button onClick={() => dispatch({ type: 'clear' })}>清空</button>
		</div>
	)
}
export default ReduxRoot
